Chain Of Responsibility Pattern

Chain of Responsibility Pattern က Behaviour Pattern တစ်ခုပါ။ Chain Of Responbility က chain တွေလိုမျိုး အဆင့် တွေ ကို တစ်ခု ပြီး တစ်ခု အလုပ်လုပ်သွားသည့် ပုံစံမျိုးပါ။ Handler က successor ကို ထပ်ခါ ထပ်ခါ ခေါ်သွားသည့် ပုံစံမျိုးပါ။

အောက်က ပုံလေးကို ကြည့် လိုက်ရင် နားလည် ပါလိမ့်မယ်။

ဒီပုံလေးကို မြင်လိုက်တာ နဲ့ backend developer တွေ ဆိုရင် Middleware ပဲ ဆိုတာ မြင်သွားပါလိမ့်မယ်။ Request တစ်ခု ရောက်လာရင် middleware ခံပြီးတော့ မှန် ခဲ့ ရင် next ကို ခေါ်ခေါ်သွားတာကို သတိရမှာပါ။

backend request ရောက်လာရင် login ဝင်ထားလား။ မှန်ရင် next ။ admin user ဖြစ်လား။ မှန်ရင် next။ ဒီ အတွက် permission ရှိလား မှန်ရင် next နဲ့ သွားပြီးတော့ file response ကို ပြပေးတာပါ။

express မှာ ဆိုရင်

app.use((req, res, next) => {
  // do something before the request is handled
  next();
});

အဲဒီ code က Chain Of Responsibility pattern ပါပဲ။ အခု အဲဒီ pattern ကို အသုံးပြုပြီး middleware ပုံစံ အကြမ်းလေး တစ်ခု ရေးကြည့်ရအောင်။

ဒီ code မှာ ဆိုရင် IMiddlware က Interface ဖြစ်ပြီး တကယ် handling လုပ်တာ ကတော့ AuthHandler နဲ့ Permission Handler ပါပဲ။

MiddlewareHandler -> RouteHandler -> PermissionHandler -> Finish

အဲဒီ ပုံစံ အတိုင်း အလုပ်လုပ်ပါမယ်။ MiddlewareHandler ကနေ Middleware တစ်ခု ဖြစ်သည့် Auth Handler ကို ခေါ်မယ်။ ပြီးရင်း route မှာ permission ရှိမရှိ စစ်ဖြစ်ဖို့ အတွက် PermissionHandler ကို ဆက်ခေါ်မယ်။ တကယ်လို့ အဆင်ပြေတယ် ဆိုရင် ပြီးသွားပြီပေါ့။

ပုံမှန် အားဖြင့် ဒီ pattern မသုံးပဲ ရေးသည့် အခါမှာ if else တွေ အများကြီး သုံးရပါမယ်။

if (route == "\hello") {
  if (user.type == "admin") {
    System.out.print("Success")
  }
  else  {
    System.out.print("Not allow")
  }
}
else {
 System.out.print("Not found")
}

တကယ်လို့ အခြား ထပ်ပြီးတော့ စစ်တာတွေ ထည့်မယ်။​ ဥပမာ  JWT Auth ကို စစ်တာ ထပ်ဖြည့်မယ်။ ဒါဆိုရင် if else ထဲမှာ ထပ် ပြင်ရအုံးမယ်။ တနည်းပြောရင် Open Close Principle ကို မလိုက်နာတော့ဘူး ဖြစ်သွားမယ်။ ဒီလို အခြေအနေ မှာ ဆိုရင် Chain Of Responsiblity က အသင့်တော်ဆုံးပါပဲ။

IMiddleWare.java

public abstract class IMiddleWare {

    IMiddleWare nextHandler;
    private Request request;

    IMiddleWare(IMiddleWare handler) {
        this.request = request;
        this.nextHandler = handler;
    }

    public abstract void handle(Request request);

}

RouteHandler.java

public class RouteHandler extends IMiddleWare {

    AuthHandler(IMiddleWare handler) {
        super(handler);
    }
    @Override
    public void handle(Request request) {
        if(request.url.contains("/hello")) {
            this.nextHandler.handle(request);
        }
        else {
            System.out.println("Not Found");
        }
    }
}

PermissionHandler.java

public class PermissionHandler extends IMiddleWare {
    User user;
    PermissionHandler(User user, IMiddleWare handler) {
        super(handler);
        this.user = user;
    }

    @Override
    public void handle(Request request) {
        if(this.user.type == UserType.Admin) {
            System.out.println("Success");
        }
        else {
            System.out.println("Sorry, You don't have a permission");
        }
    }
}

MiddleWareHandler.java

public class MiddleWareHandler {

    IMiddleWare handler;

    public void setHandler(IMiddleWare handler) {
        this.handler = handler;
    }

    public void handle(Request request) {
        handler.handle(request);
    }
}

Application.java

public class Application {

    public static void main(String[] args) {
        User user = new User();
        user.type = UserType.Admin;
        Request request = new Request("http://www.example/hello");

        PermissionHandler permissionHandler = new PermissionHandler(user,null);
        AuthHandler authHandler = new AuthHandler(permissionHandler);

        MiddleWareHandler middleware = new MiddleWareHandler();
        middleware.setHandler(authHandler);
        middleware.handle(request);

    }
}

Pros and Cons

Single Responsibility ဖြစ်ပါတယ်။ Chain တိုင်းမှာ သူ့ လုပ်ရမယ့် အလုပ်ကို သာ လုပ်ပါတယ်။

Open/Closed Principle ကို follow လုပ်ထားပါတယ်။

မကောင်းတာကတော့ Request တစ်ခုခု က next handling ကို မလုပ်ထားခဲ့ရင် ရပ်သွားမှာပါ။